#ifndef GS_CONTEXT_H
#define GS_CONTEXT_H

#include "include/Gs.h"

namespace Gs
{
	class GarbageCollector;
	class IObject;
	class IString;
	class Object;
	class Value;
	class IValue;
	class Function;
	class String;

	// Defines the default stack size in elements.
	const int DefaultStackSize = 32;

	// Defines the minimum stack growth size in elements (reduces reallocations).
	const int MinimumStackGrowth = 8;

	// Represents a GorillaScript runtime context.
	class Context : public IContext
	{
	private:
		// Stores the managed objects for garbage collection.
		GarbageCollector *_garbage;

		// Stores the global named values.
		Object *_global;

		// Stores the values in the stack.
		Value *_stack;

		// Stores the size of the stack.
		int _stackSize;

		// Stores the top index in the stack.
		int _stackTop;

		// Stores the current stack frame index.
		int _stackFrame;

		friend class GarbageCollector;

	public:
		// Initialises a new instance of the IContext class.
		Context();

		// Destroys the instance.
		~Context();

		// Creates an unreferenced object.
		IObject *CreateObject();

		// Creates an unreferenced string.
		IString *CreateString(const char *str);

		// Creates an unreferenced string.
		String *CreateString(unsigned int size);

		// Creates an unreferenced function.
		Function *CreateFunction(NativeFunction f = 0);
		
		// Compiles a script into a function on the stack.
		void CompileScript(const char *script, const char *sourceName);

		// Compiles a script into a function on the stack using a file.
		void CompileScriptFromFile(const char *filename);

		// Calls the function on the stack. The function should be pushed before arguments.
		void Call(int arguments);

		// Gets a pointer to the global object.
		IObject *GetGlobalObject() const;

		// Pops and assigns a value to a name in the global object.
		void SetGlobal(const char *name);

		// Pops and assigns a value to a name in the global object.
		void SetGlobal(IString *name);

		// Pushes a global value onto the stack.
		void PushGlobal(const char *name);

		// Pushes a global value onto the stack.
		void PushGlobal(IString *name);
		
		// Gets the relative index to the top value on the stack.
		int Top() const;

		// Gets the value at the specified index in the stack.
		IValue *ValueAt(int index = -1) const;

		// Gets the value at the absolute index in the stack.
		Value *ValueAtAbs(int index) const;

		// Pops the top value off the stack.
		void Pop();

		// Pushes a nil value onto the stack.
		void Push();

		// Pushes an integer onto the stack.
		void Push(int i);

		// Pushes a float onto the stack.
		void Push(float i);

		// Pushes a string onto the stack.
		void Push(const char *s);

		// Pushes a string onto the stack.
		void Push(IString *s);

		// Pushes an object onto the stack.
		void Push(IObject *o);

		// Pushes a native function onto the stack.
		void Push(NativeFunction f);

		// Pushes a user pointer onto the stack.
		void Push(UserPointer up);

		// Pushes a value onto the stack.
		void Push(IValue *v);

		// Pushes the stack frame onto the stack.
		void PushFrame();

		// Pops the stack frame off the stack.
		void PopFrame();

		// Gets the value at the newly checked and pushed index.
		Value *CheckedPush();
	};
}

#endif